using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Sys.Web.Ajax
{
  [DefaultProperty("Text")]
  [ToolboxData("<{0}:InlineEditBox runat=server></{0}:InlineEditBox>")]
  public class InlineEditBox : TextBox
  {
    #region Public Enums

    public enum InlineClickMode
    {
      SingleClick = 1,
      DoubleClick = 2
    }

    public enum InlineEditBoxMode
    {
      AdminMode = 1,
      DisplayMode = 2
    }

    #endregion

    #region Public Properites

    [DefaultValue(1)]
    public InlineClickMode ClickMode
    {
      get
      {
        if (ViewState["_ClickMode"] != null)
          return (InlineClickMode)((int)ViewState["_ClickMode"]);
        else
          return InlineClickMode.SingleClick;
      }
      set { ViewState["_ClickMode"] = (int)value; }
    }

    [DefaultValue("(Click to enter text)")]
    public string DefaultBlankText
    {
      get
      {
        String s = (String)ViewState["_DefaultBlankText"];
        return (s ?? "(Click to enter text)");
      }
      set { ViewState["_DefaultBlankText"] = value; }
    }

    [DefaultValue("")]
    public string LabelCssClass
    {
      get
      {
        String s = (String)ViewState["_LabelCssClass"];
        return (s ?? String.Empty);
      }
      set { ViewState["_LabelCssClass"] = value; }
    }

    [DefaultValue("")]
    public string LabelHoverCssClass
    {
      get
      {
        String s = (String)ViewState["_LabelHoverCssClass"];
        return (s ?? String.Empty);
      }
      set { ViewState["_LabelHoverCssClass"] = value; }
    }

    [Bindable(true)]
    [Category("Appearance")]
    [DefaultValue("")]
    [Localizable(true)]
    public override string Text
    {
      get
      {
        String s = (String)ViewState["Text"];
        return (s ?? String.Empty);
      }
      set
      {
        ViewState["Text"] = value;
      }
    }

    [DefaultValue("")]
    public string CallBackFunction
    {
      get
      {
        String s = (String)ViewState["_CallBackFunction"];
        return ((s == null) ? String.Empty : s);
      }
      set { ViewState["_CallBackFunction"] = value; }
    }

    [DefaultValue(false)]
    public Boolean CallBackOnBlur
    {
      get
      {
        if (ViewState["_CallBackOnBlur"] != null)
          return (Boolean)ViewState["_CallBackOnBlur"];
        else return false;
      }
      set { ViewState["_CallBackOnBlur"] = value; }
    }

    [DefaultValue(false)]
    public Boolean CallBackIfIdentical
    {
      get
      {
        if (ViewState["_CallBackIfIdentical"] != null)
          return (Boolean)ViewState["_CallBackIfIdentical"];
        else
          return false;
      }
      set { ViewState["_CallBackIfIdentical"] = value; }
    }

    [DefaultValue(false)]
    public Boolean ResizeTextboxWidth
    {
      get
      {
        if (ViewState["_ResizeTextboxWidth"] != null)
          return (Boolean)ViewState["_ResizeTextboxWidth"];
        else
          return false;
      }
      set { ViewState["_ResizeTextboxWidth"] = value; }
    }

    [DefaultValue(true)]
    public Boolean ResizeTextboxHeight
    {
      get
      {
        if (ViewState["_ResizeTextboxHeight"] != null)
          return (Boolean)ViewState["_ResizeTextboxHeight"];
        else
          return true;
      }
      set { ViewState["_ResizeTextboxHeight"] = value; }
    }

    [DefaultValue(true)]
    public Boolean TrapEnterKey
    {
      get
      {
        if (ViewState["_TrapEnterKey"] != null)
          return (Boolean)ViewState["_TrapEnterKey"];
        else
          return true;
      }
      set { ViewState["_TrapEnterKey"] = value; }
    }

    [DefaultValue(true)]
    public Boolean TrapShiftEnterKey
    {
      get
      {
        if (ViewState["_TrapShiftEnterKey"] != null)
          return (Boolean)ViewState["_TrapShiftEnterKey"];
        else
          return true;
      }
      set { ViewState["_TrapShiftEnterKey"] = value; }
    }

    [DefaultValue(true)]
    public Boolean TrapEscapeKey
    {
      get
      {
        if (ViewState["_TrapEscapeKey"] != null)
          return (Boolean)ViewState["_TrapEscapeKey"];
        else
          return true;
      }
      set { ViewState["_TrapEscapeKey"] = value; }
    }

    [DefaultValue("")]
    public string ClientIDSuffix
    {
      get
      {
        String s = (String)ViewState["_ClientIDSuffix"];
        return (s ?? String.Empty);
      }
      set { ViewState["_ClientIDSuffix"] = value.Replace(":", "").Replace(" ", ""); }
    }

    [DefaultValue(1)]
    public InlineEditBoxMode Mode
    {
      get
      {
        if (ViewState["_Mode"] != null)
          return (InlineEditBoxMode)((int)ViewState["_Mode"]);
        else
          return InlineEditBoxMode.AdminMode;
      }
      set { ViewState["_Mode"] = (int)value; }
    }

    #endregion

    #region Protected Override Functions

    protected override void OnInit(EventArgs e)
    {
      base.OnInit(e);

      if (HttpContext.Current == null)
        return;

      ScriptManager current = ScriptManager.GetCurrent(this.Page);
      if (current == null)
        throw new Exception("The page must contain a ScriptManager or ScriptManagerProxy control.");

      // Display: none now being controlled through JS for gracefully downgrading //

      /*if (base.Attributes["style"] != null)
          base.Attributes["style"] = base.Attributes["style"].Equals("") ? "display:none;" : base.Attributes["style"].Substring(base.Attributes["style"].Length - 1, 1).Equals(";") ? base.Attributes["style"] + "display:none;" : base.Attributes["style"] + ";display:none;";
      else
          base.Attributes.Add("style", "display:none;");*/

      if ((this.CallBackOnBlur) && (this.CallBackFunction.Equals("")))
        throw new Exception("Callback Function cannot be null or empty.");

    }

    protected override void OnLoad(EventArgs e)
    {
      if (HttpContext.Current == null)
        return;

      base.OnLoad(e);
    }

    public override void RenderControl(HtmlTextWriter writer)
    {
      this.ID += this.ClientIDSuffix;

      if (HttpContext.Current == null)
      {
        this.Text = "[ " + this.ID + " ]";
      }

      base.RenderControl(writer);

      if (HttpContext.Current != null)
      {
        string script = BuildInitScript();
        ScriptManager.RegisterStartupScript(this.Page, typeof(Page), this.ID + "client", script, true);
      }
      else
        this.Text = "";
    }

    protected override void RenderContents(HtmlTextWriter output)
    {
      output.Write(Text);
    }

    #endregion

    #region Private Functions

    private string BuildInitScript()
    {
      // Generate script variable //
      string script = "";

      // Create the inline edit's javascript object //
      script += @"
                var " + this.ID + " = {" + @"
	                init : function()
	                {
                        this.pressedEscape = false;
                        this.textbox = $get('" + this.ClientID + @"');
                        this.textbox.style.display = 'none';
                        this.label = document.createElement('span');
                        this.label.id = 'lbl" + this.ID + @"';
                        this.label.className = '" + this.LabelCssClass + @"';
                        this.label.innerHTML = '" + (this.Text.Equals("") ? this.DefaultBlankText.Replace(Environment.NewLine, "<br />").Replace("'", @"\'") : this.Text.Replace(Environment.NewLine, "<br />").Replace("'", @"\'")) + @"'
                        this.textbox.parentNode.insertBefore(this.label, this.textbox.nextSibling);
                ";

      if (!this.LabelHoverCssClass.Equals("") && this.Mode == InlineEditBoxMode.AdminMode)
      {
        script += @"
                        $addHandler(this.label, 'mouseover', function() { this.style.cursor = 'default'; Sys.UI.DomElement.addCssClass(this,'" + this.LabelHoverCssClass + @"'); });";

        script += @"
                        $addHandler(this.label, 'mouseout', function() { Sys.UI.DomElement.removeCssClass(this,'" + this.LabelHoverCssClass + @"'); });";
      }
      else if (this.Mode == InlineEditBoxMode.AdminMode)
      {
        script += @"
                        $addHandler(this.label, 'mouseover', function() { this.style.cursor = 'default'; });";
      }

      if (this.ClickMode == InlineClickMode.SingleClick && this.Mode == InlineEditBoxMode.AdminMode)
      {
        script += @"
                        $addHandler(this.label, 'click', " + this.ID + @".click);";
      }
      else if (this.ClickMode == InlineClickMode.DoubleClick && this.Mode == InlineEditBoxMode.AdminMode)
      {
        script += @"
                        $addHandler(this.label, 'dblclick', " + this.ID + @".click);";
      }

      if (this.Mode == InlineEditBoxMode.AdminMode)
      {
        script += @"
                        $addHandler(this.textbox, 'blur', " + this.ID + @".blur);
                        $addHandler(this.textbox, 'keydown', " + this.ID + @".keydown);
                    },";
      }
      else
        script += @"
                    }";

      if (this.Mode == InlineEditBoxMode.AdminMode)
      {
        script += @"
                    click : function(event)
                    {
                        var textbox = " + this.ID + @".textbox;
                        if (event.target.innerHTML.toLowerCase().indexOf('" + this.DefaultBlankText.ToLower() + @"') != -1 && '" + this.DefaultBlankText.ToLower() + @"' != '')
                            textbox.value = '';
                        else
                        {
                            var newValue = event.target.innerHTML;
	                        while((newValue.indexOf('<br />') > -1) || (newValue.indexOf('<br>') > -1) || (newValue.indexOf('<BR>') > -1) || (newValue.indexOf('<BR />') > -1)){ newValue = newValue.replace('<br />','\n').replace('<br>','\n').replace('<BR />','\n').replace('<BR>','\n'); }
                            textbox.value = newValue;
                        }";

        if (this.ResizeTextboxHeight)
        {
          script += @"
                        textbox.style.height = Sys.UI.DomElement.getBounds(event.target).height + 'px';";
        }

        if (this.ResizeTextboxWidth)
        {
          script += @"
                        textbox.style.width = Sys.UI.DomElement.getBounds(event.target).width + 'px';";
        }

        script += @"
                        event.target.style.display = 'none';
                        textbox.style.display = '';
                        textbox.focus();
                    },

                    blur : function(event)
                    {
                        var isIdentical = false;
                        if (!" + this.ID + ".pressedEscape)";
        script += @"
                        {
                            var label = " + this.ID + @".label;
                            var newValue = '';
                            var oldValue = label.innerHTML;

                            if (event.target.value.replace(/^\s+|\s+$/g,'') == '')
                            {
                                newValue = '';
                                label.innerHTML = '" + this.DefaultBlankText + @"';
                            }
                            else
                            {
                                newValue = event.target.value;
                                while(newValue.indexOf('\r') > -1){ newValue = newValue.replace('\r', ''); }
                                while(newValue.indexOf('\n') > -1){ newValue = newValue.replace('\n', " + "\"<br />\"" + @"); }
                                label.innerHTML = newValue;
                            }

                            if (oldValue == label.innerHTML)
                                isIdentical = true;

                            event.target.style.display = 'none';
                            label.style.display = '';
                            event.target.value = newValue;";

        if (this.CallBackOnBlur)
        {
          if (this.CallBackIfIdentical)
          {
            script += @"
                            eval('" + this.CallBackFunction + "(newValue, \"" + this.ClientID + "\")');";
          }
          else
          {
            script += @"
                            if (!isIdentical)
                                eval('" + this.CallBackFunction + "(label.innerHTML, \"" + this.ClientID + "\")');";
          }
        }

        script += @"
                        }
                        else
                           " + this.ID + @".pressedEscape = false;
                    },

                    keydown : function(event)
                    {
                        var keynum = event.keyCode;
                        ";

        if (this.TrapShiftEnterKey)
        {
          script += @"
                        if ((keynum == 13) && (event.shiftKey))
                            return;";
        }

        if (this.TrapEnterKey)
        {
          script += @"
                        if (keynum == 13) { // Enter Key
                            event.preventDefault();
                            event.returnValue = false;
                            event.target.blur();
                            return;
                        }";
        }

        if (this.TrapEscapeKey)
        {
          script += @"
                        if (keynum == 27) { // Escape Key
                            event.preventDefault();
                            event.returnValue = false;
                            " + this.ID + @".pressedEscape = true;
                            event.target.style.display = 'none';
                            " + this.ID + @".label.style.display = '';
                            return;
                        }";
        }

        script += @"
                }";
      }
      script += @"
                }
            " + this.ID + @".init();
            ";

      return script;
    }

    #endregion

  }
}